package com.smsc.headend.task.engine.executor.impl.generalparam;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.DLMSSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.generalparam.ActionGeneralParamHandler;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import gurux.dlms.enums.ObjectType;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author wxh
 * @date 2021/5/11 14:06
 */
public class ActionGeneralParamExecutor implements TaskExecutor {
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    AssetService assetService;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());

        Meter meter = getMeter(task, assetService);
        MeterConfigDMO meterConfigDMO = task.getMeterId() == null ? null : assetService.getMeterConfigById(task.getMeterId());
        ComEntity terminal = assetService.getComById(task.getComId());
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());
        List<DataItemDTO> dataItemCosemList = dataItemFacade.getDataItemsByOperation(meter, terminal, dictionary, getParameterMap(task, atomicTask), comConfigDMO.getModelId());
        if (CollUtil.isEmpty(dataItemCosemList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.NO_DATA_ITEM.toString());
        }
        DeviceOperation operationGroup = assetService.getDictionary(atomicTask.getGroupId(), DictionaryType.OperationGroup.getType());
        DeviceSAPandKeys deviceKeys = meter != null ? assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), dataItemCosemList.get(0).getRoleId()) :
                assetManagementRemoteService.getTerminalCommunicationKey(terminal.getComId(), dataItemCosemList.get(0).getRoleId());
        byte[][] packets = getPacketByOperationGroup(atomicTask, meterConfigDMO, operationGroup, dataItemCosemList, meter, deviceKeys);
        if (packets == null) {
            log.error("packet is null");
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.BUILD_PACKET_ERROR.toString());
        }
        log.info(ByteUtils.byteToHexString(packets[0]));
        connectorService.sendPacket(task, atomicTask, dataItemCosemList.get(0).getRoleId(), task.getIsODR(), packets);

        return createAtomicState(ActionGeneralParamHandler.class);
    }

    private byte[][] getPacketByOperationGroup(AtomicTask atomicTask, MeterConfigDMO meterConfigDMO, DeviceOperation operationGroup, List<DataItemDTO> dataItemCosemList, Meter meter, DeviceSAPandKeys deviceKeys) throws UDISTaskExecutorException {
        byte[][] packet = null;
        OperationGroup operationGp = OperationGroup.forId(operationGroup.getOptId().intValue());
        switch (operationGp) {
            case GeneralParametersAction:
                packet = actionGeneralParameter(atomicTask, meterConfigDMO, meter, dataItemCosemList, deviceKeys);
                break;
        }
        return packet;
    }

    private byte[][] actionGeneralParameter(AtomicTask atomicTask, MeterConfigDMO meterConfigDMO, Meter meter, List<DataItemDTO> dataItemCosemList, DeviceSAPandKeys deviceKeys) throws UDISTaskExecutorException {
        byte[][] packet = null;
        DataItemDTO dataItemDto = dataItemCosemList.get(0);
        Map<String, Object> map = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
        switch (ObjectType.forValue(((DLMSSubDataItemDTO) dataItemDto).getClassId())){
            case TOKEN_GATEWAY:
                // token为20位数字
                if (!Pattern.matches("[0-9]{20}", Convert.toStr(map.get("value")))) {
                    throw new UDISTaskExecutorException(TaskExecuteErrorCode.INVALID_TOKEN.toString());
                }
                packet =  new byte[][] {packetService.actionTokenDataPacket(dataItemCosemList, meter, map.get("value"), meterConfigDMO, deviceKeys)};
                break;
            default:
                throw new UDISTaskExecutorException(TaskExecuteErrorCode.NO_IMPLEMENTATION_CURRENTLY.toString());
        }
        return packet;
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        Map<String, Object> param = getParameterMap(task, atomicTask);
        Long subDataItemId = Long.valueOf(String.valueOf(param.get("subDataItemId"))) ;
        if (task.getIsODR()) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(subDataItemId), message, 600L, false);
        }
        return true;
    }
}
